package com.aerfazhe.order.controller;

import com.aerfazhe.order.config.FeignConfig;
import com.aerfazhe.order.feign.StockClient;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.data.RepositoryType;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author：wk
 * @Create：2022/4/10/11:17
 * @Description：TODO
 * @Version：1.0
 */
@RestController
@RequestMapping("/order")
@RefreshScope
@Slf4j
public class OrderController {

//    private final static String STOCK_URL = "http://localhost:8082/stock/reduct";
//    Nacos服务
//    private final static String STOCK_URL = "http://stock-service/stock/reduct";

    private final static String RESOURCE_NAME = "findAll";

//    @Autowired
//    private RestTemplate restTemplate;

//    @Value("${user.name}")
    private String userName;

//    @Value("${user.sex}")
    private String userSex;


    @Autowired
    private StockClient stockClient;

    @GetMapping("/add")
    public String add() {
        System.out.println("下单成功");
//        String msg = restTemplate.getForObject(STOCK_URL, String.class);
        String msg = stockClient.reduct();
        return "下单成功!"+msg;
    }

    /**
     * 测试 X-Request-color 请求头
     * @param color
     * @return
     */
    @GetMapping("/header")
    public String header(@RequestHeader("X-Request-color") String color) {
        log.info("获取的请求头的值："+color);
        return color;
    }

    /**
     * 定义流控规则
     * Spring的初始化方法
     */
//    @PostConstruct // init-method
    public static void initFlowRules() {
//        流控规则
        List<FlowRule> ruleList = new ArrayList<>();
//        流控
        FlowRule flowRule = new FlowRule();
//        访问受保护的资源
        flowRule.setResource("getMapper");
//        设置流控规则 OPS
        flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//        设置受保护资源的流量阀值
        flowRule.setCount(1);
//        加入流控规则
        ruleList.add(flowRule);
//        加载流控规则
        FlowRuleManager.loadRules(ruleList);
    }

    /**
     * 定义降级规则
     * Spring的初始化方法
     */
    @PostConstruct // init-method
    public static void initDegradeRules() {
//        流控规则
        List<DegradeRule> ruleList = new ArrayList<>();
//        流控
        DegradeRule degradeRule = new DegradeRule();
//        访问受保护的资源
        degradeRule.setResource("getMapper");
//        设置降级规则侧率： 异常数
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//        触发熔断异常数
        degradeRule.setCount(2);
//        触发熔断的最小请求数
        degradeRule.setMinRequestAmount(2);
//        统计时长：在多长时间内触发请求和异常数  单位：ms 当前设置1分钟
        degradeRule.setStatIntervalMs(60*1000); // 时间太短不好测
//        上部分配置，整体表示在一分钟内请求了2次，触发异常数2次，就会触发熔断
//         熔断持续时间（秒）再次请求对应接口，期间就会直接调用降级方法
//        时间过后进行----半开状态，如果接口第一次请求还出现异常，再次熔断，不会根据设置的条件进行判断
        degradeRule.setTimeWindow(5);
//        加入降级规则
        ruleList.add(degradeRule);
//        加载降级规则
        DegradeRuleManager.loadRules(ruleList);
    }

    @GetMapping("/findAll")
    public String findAll() {
//        Sentinel针对资源进行限制，需要定义资源名
        Entry entry = null;
        try {
            entry = SphU.entry(RESOURCE_NAME);
        } catch (BlockException e) {
            return "被流控啦！";
        }
        System.out.println("查询所有订单");
        return "查询所有订单！";
    }

    /**
     * @SentinelResource 改善接口中的资源定义和被流控降级后的处理方法
     *  value： 定义资源名称
     *  blockHandler：设置 流控、降级后的处理方法（默认该方法必须声明在同一个类中）
     *  blockHandlerClass: 如果将流控、降级处理方法设置在其他类中，则需要指定其他类，且流控、降级方法需要加上static静态
     *  fallback: 处理接口异常时指定的方法
     * @return
     */
    @GetMapping("/getMapper")
    @SentinelResource(value = "getMapper",entryType = EntryType.IN,blockHandler = "degrade")
    public String getMapper() {
        throw new RuntimeException("异常");
//        return "Hello World"+userName+userSex;
    }

    /**
     * 注意：
     * 1、一定要public
     * 2、返回值要和源方法保持一致,包含源方法的参数且顺序要保持一致
     * 3、可以在参数最后添加 BlockException区分是什么规则的处理方法
     * 降级方法
     * @return
     */
    public String degrade(BlockException e) {
        e.printStackTrace();
        return "被降级喽！"+userName+userSex;
    }

}
